package com.quan.commons.exception;

import com.alibaba.fastjson.JSON;
import com.quan.commons.constant.CommonsConstant;
import com.quan.commons.log.LogVo;
import com.quan.commons.utils.JwtUtils;
import com.quan.commons.vo.R;
import com.quan.system.entity.SysLog;
import com.quan.system.service.SysLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.yaml.snakeyaml.constructor.DuplicateKeyException;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 统一异常处理
 */
@Slf4j
@RestControllerAdvice
public class ExceptionControllerAdvice {

    @Autowired
    private SysLogService service;

    /**
     * VO数据校验异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handleVaildException(MethodArgumentNotValidException e) {
        log.error("数据校验出现问题：{}", e.getMessage());

        Map<String, Object> errorMap = new HashMap<String, Object>();

        BindingResult bindingResult = e.getBindingResult();
        bindingResult.getFieldErrors().forEach(fieldError -> {
            errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
        });

        return R.error(CommonsConstant.BizErrorEnums.VAILD_EXCEPTION.getCode(), CommonsConstant.BizErrorEnums.VAILD_EXCEPTION.getMsg()).put("data", errorMap);
    }

    /**
     * 数据库中已存在该记录异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler(DuplicateKeyException.class)
    public R handleDuplicateKeyException(DuplicateKeyException e) {
        log.error("数据库中已存在该记录：{}", e.getMessage(), e);
        return R.error("数据库中已存在该记录");
    }

    /**
     * 业务异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler(BizException.class)
    public R handleBizException(BizException e) {
        log.error("业务异常：{}", e.getMessage());
        return R.error(e.getMessage());
    }

    /**
     * 任意异常处理
     *
     * @param throwable
     * @return
     */
    @ExceptionHandler(value = Throwable.class)
    public R handleException(Throwable throwable) {
        final Class<? extends Throwable> clazz = throwable.getClass();

        log.error("程序异常：{}，异常类型：{}", throwable.getMessage(), clazz);
        log.error("异常详细信息：{}", throwable);

        String msg = throwable.getMessage();
        if (null == msg) {
            msg = "程序缺少参数";
        }

        // 保存异常数据到数据库
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

        String token = request.getHeader("token");

        Long userId = Long.parseLong(JwtUtils.getUserId(token));
        String userName = JwtUtils.getUserName(token);

        String ipAddr = this.getRemoteHost(request);

        LogVo vo = new LogVo();
        vo.setType(CommonsConstant.LogTypeEnums.EXCEPTION.getIndex());
        vo.setUserId(userId);
        vo.setUserName(userName);
        vo.setIp(ipAddr);
        vo.setRequestUri(request.getRequestURI());
        vo.setRequestMethod(request.getMethod());
        vo.setUserAgent(request.getHeader("User-Agent"));
        vo.setException(throwable.getLocalizedMessage());

        SysLog log = new SysLog();
        BeanUtils.copyProperties(vo, log);
        this.service.save(log);

        return R.error(CommonsConstant.BizErrorEnums.UNKNOW_EXCEPTION.getCode(), msg);
    }

    /**
     * 获取目标主机的ip
     *
     * @param request 请求
     */
    private String getRemoteHost(HttpServletRequest request) {
        if (request == null) {
            return "";
        }

        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

}
